package czopyk.model.persistence;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import org.xml.sax.Attributes;

import pl.czopyk.colt.math.Constant;
import pl.czopyk.colt.math.DistributionWrapper;
import pl.czopyk.colt.math.Weibull;
import czopyk.model.factories.Utils;
import cern.jet.random.AbstractDistribution;
import cern.jet.random.Beta;
import cern.jet.random.BreitWigner;
import cern.jet.random.ChiSquare;
import cern.jet.random.Empirical;
import cern.jet.random.EmpiricalWalker;
import cern.jet.random.Exponential;
import cern.jet.random.ExponentialPower;
import cern.jet.random.Gamma;
import cern.jet.random.Hyperbolic;
import cern.jet.random.Logarithmic;
import cern.jet.random.Normal;
import cern.jet.random.StudentT;
import cern.jet.random.Uniform;
import cern.jet.random.VonMises;
import cern.jet.random.engine.RandomEngine;

public class Helper {

	private static final Map<Class<? extends AbstractDistribution>, String[]> distributionMapping = new HashMap<Class<? extends AbstractDistribution>, String[]>();
	private static final String[] DIST_PACKAGES = "cern.jet.random.|pl.czopyk.colt.math.".split("\\|");
	private static final String AVAILABLE_DISTRIBUTIONS;

	static {
		distributionMapping.put(Normal.class, "mean sd".split(" "));
		distributionMapping.put(Uniform.class, "min max".split(" "));
		distributionMapping.put(Beta.class, "alpha beta".split(" "));
		distributionMapping.put(Weibull.class, "alpha beta".split(" "));
		distributionMapping.put(BreitWigner.class, "mean gamma cut".split(" "));
		distributionMapping.put(ChiSquare.class, "freedom".split(" "));
		distributionMapping.put(Exponential.class, "lambda".split(" "));
		distributionMapping.put(ExponentialPower.class, "tau".split(" "));
		distributionMapping.put(Gamma.class, "alpha lambda".split(" "));
		distributionMapping.put(Hyperbolic.class, "alpha beta".split(" "));
		distributionMapping.put(Logarithmic.class, "p".split(" "));
		distributionMapping.put(StudentT.class, "freedom".split(" "));
		distributionMapping.put(VonMises.class, "freedom".split(" "));
		distributionMapping.put(Constant.class, "value".split(" "));

		StringBuilder sb = new StringBuilder();
		for (Class<?> cl : distributionMapping.keySet().toArray(new Class<?>[0])) {
			sb.append(cl.getSimpleName() + ", ");
		}
		sb.deleteCharAt(sb.length() - 1);
		sb.deleteCharAt(sb.length() - 1);
		AVAILABLE_DISTRIBUTIONS = sb.toString();
	}

	public static DistributionWrapper buildDistribution(final Attributes atts) {
		String type = atts.getValue("type");
		type = correctCase(type);

		// przypadek szczegolny
		if ("empirical".equalsIgnoreCase(type)) {
			String[] ss = atts.getValue("pdf").split("\\|");

			String[] tmp;
			int max = 0;
			for (int i = 0; i < ss.length; ++i) {
				tmp = ss[i].split(",");
				if (Integer.valueOf(tmp[0]) > max) {
					max = Integer.valueOf(tmp[0]);
				}
			}

			final double[] pdf = new double[max + 1];
			Arrays.fill(pdf, 0.0);
			int index;
			double df;
			for (int i = 0; i < ss.length; ++i) {
				tmp = ss[i].split(",");
				index = Integer.valueOf(tmp[0]);
				df = Double.valueOf(tmp[1]);
				pdf[index] = df;
			}
			return new DistributionWrapper() {
				private static final long serialVersionUID = -6590398155857274440L;

				{
					delegate = new EmpiricalWalker(pdf, Empirical.NO_INTERPOLATION, Utils.getNewRandomEngine());
					offset = atts.getValue("offset") != null ? Double.valueOf(atts.getValue("offset")) : 0.0;
				}
			};
		}
		
		try {
			final Class<?> c = getClazz(type);
			
			final Class<?>[] argTypes = new Class<?>[distributionMapping.get(c).length + 1];
			Arrays.fill(argTypes, Double.TYPE);
			argTypes[argTypes.length - 1] = RandomEngine.class;
			
			final Object[] arg = new Object[argTypes.length];
			int i = 0;
			for (String s : distributionMapping.get(c)) {
				arg[i++] = Double.valueOf(atts.getValue(s));
			}
			arg[i] = Utils.getNewRandomEngine();
			
			return new DistributionWrapper() {
				private static final long serialVersionUID = 8816611043314081026L;

				{
					delegate = (AbstractDistribution) c.getConstructor(argTypes).newInstance(arg);
					offset = atts.getValue("offset") != null ? Double.valueOf(atts.getValue("offset")) : 0.0;
				}
			};
		} catch (NoSuchMethodException e) {
			throw new IllegalArgumentException("Implementation of distribution: '" + type
					+ "' lacks a proper constructor");
		}
		catch (Exception e) {
			throw new IllegalArgumentException("Unknown distribution: " + type
					+ ". Avalible distributions are: " + AVAILABLE_DISTRIBUTIONS);
		}
	}
	
	private static String correctCase(String type) {
		for (Class<?> c : distributionMapping.keySet()) {
			if (type.equalsIgnoreCase(c.getSimpleName())) {
				return c.getSimpleName();
			}
		}
		return type;
	}

	private static Class<?> getClazz(String type) throws ClassNotFoundException {
		Class<?> clazz = null;
		for (String packageName : DIST_PACKAGES) {
			try {
				clazz = Class.forName(packageName + type);
				if (clazz != null){
					break;
				}
			} catch (Exception e) {
				;
			}
		}
		if (clazz == null){
			throw new ClassNotFoundException("No class for name '" + type + "' found.");
		}
		return clazz;
	}

	public static void main(String[] args) throws ClassNotFoundException {
		System.out.println(getClazz("Constant"));
	}
	
}
